#pragma once

#include <cstring>
#include <sys/cdefs.h>
namespace xncc {
namespace msg {

    template <class MSG, unsigned int SIZE>
    class SpmcQueue
    {
        static_assert(SIZE && !(SIZE & (SIZE - 1)), "size must be power of 2");

      public:
        void write(const MSG* msg)
        {
            auto& blk = blks_[(write_index_ + 1) / SIZE];
            memcpy(&blk.msg, msg, sizeof(MSG));
            asm volatile("" : : "m"(blk) :);
            blk.index = ++write_index_;
            asm volatile("" : : "m"(write_index_), "m"(blk) :);
        }

        struct Reader
        {
            SpmcQueue<MSG, SIZE>* queue = nullptr;
            unsigned int          read_index;

            MSG* read()
            {
                auto& blk = queue->blks_[(read_index + 1) % SIZE];
                asm volatile("" : "=m"(blk)::);
                unsigned int new_idx = blk.index;
                if (int(new_idx) - int(read_index) <= 0) {
                    return nullptr;
                }
                read_index = new_idx;
                return &(blk.msg);
            }
        };

        Reader getReader()
        {
            Reader reader;
            reader.queue      = this;
            reader.read_index = write_index_;
            return reader;
        }

      private:
        friend struct Reader;
        struct __attribute__((aligned(64))) Block
        {
            unsigned int index = 0;
            MSG          msg;
        };
        Block                                               blks_[SIZE];
        __attribute__((aligned(128))) volatile unsigned int write_index_;
    };
}  // namespace msg
}  // namespace xncc